//
// Created by QU on 24-10-22.
//

#include <iostream>
#include <format>
#include <chrono>
#include <thread>
#include <random>
#include <vector>

#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio.hpp>

namespace asio = boost::asio;
namespace this_coro = asio::this_coro;

using asio::awaitable;
using asio::co_spawn;
using asio::detached;
using asio::use_awaitable;
using tcp = asio::ip::tcp;

// 辅助函数：获取当前时间戳
std::string get_timestamp() {
    auto now = std::chrono::system_clock::now();
    return std::format("{:%Y-%m-%d %H:%M:%S}", now);
}

// 辅助函数：获取当前线程ID
std::string get_thread_id() {
    return std::format("{}", std::this_thread::get_id());
}

// 日志输出函数
template<typename... Args>
void log(std::string_view fmt, const Args&... args) {
    auto formatted = std::vformat(fmt, std::make_format_args(args...));
    std::cout << std::format("[{}][Thread {}] {}\n",
                             get_timestamp(), get_thread_id(), formatted);
}

// 生成指定长度的随机数据
std::vector<char> generate_random_data(size_t length) {
    static std::mt19937 gen(std::random_device{}());
    static std::uniform_int_distribution<> dis(0, 255);

    std::vector<char> data(length);
    for (auto& byte : data) {
        byte = static_cast<char>(dis(gen));
    }
    return data;
}

awaitable<void> client(tcp::socket socket, size_t data_length, int iterations) {
    try {
        for (int i = 0; i < iterations; ++i) {
            // 生成随机数据
            auto send_data = generate_random_data(data_length);
            log("Sending {} bytes of random data", send_data.size());

            // 发送数据
            co_await async_write(socket, asio::buffer(send_data), use_awaitable);

            // 接收 echo 数据
            std::vector<char> recv_data(data_length);
            size_t received = co_await socket.async_read_some(asio::buffer(recv_data), use_awaitable);

            log("Received {} bytes of echo data", received);

            // 验证接收的数据
            if (std::equal(send_data.begin(), send_data.end(), recv_data.begin())) {
                log("Echo data matches sent data");
            } else {
                log("Error: Echo data does not match sent data");
            }

            // 等待一小段时间再发送下一批数据
            co_await asio::steady_timer(socket.get_executor(), std::chrono::seconds(1)).async_wait(use_awaitable);
        }

        log("Client finished sending all data");
    }
    catch (std::exception& e) {
        log("Exception in client coroutine: {}", e.what());
    }
}

awaitable<void> run_client(std::string host, std::string port, size_t data_length, int iterations) {
    auto executor = co_await this_coro::executor;
    tcp::resolver resolver(executor);

    log("Resolving hostname: {}", host);
    auto endpoints = co_await resolver.async_resolve(host, port, use_awaitable);

    log("Connecting to server");
    tcp::socket socket(executor);
    co_await asio::async_connect(socket, endpoints, use_awaitable);

    log("Connected to server, starting client coroutine");
    co_await client(std::move(socket), data_length, iterations);
}

int main(int argc, char* argv[]) {
    if (argc != 5) {
        std::cerr << "Usage: client <host> <port> <data_length> <iterations>\n";
        return 1;
    }

    std::string host = argv[1];
    std::string port = argv[2];
    size_t data_length = std::stoul(argv[3]);
    int iterations = std::stoi(argv[4]);

    try {
        asio::io_context io_context(1);

        log("Starting client");
        co_spawn(io_context, run_client(host, port, data_length, iterations), detached);

        io_context.run();

        log("Client finished");
    }
    catch (std::exception& e) {
        log("Exception in main: {}", e.what());
    }

    return 0;
}
